<?php
/**
 * Classe singleton di utilità per la gestione della connessione alla base dati e delle query.
 *
 * @author Damiano Ciarla, Fabrizio Filieri
 * @version 1.0
 * @category lib/database
 */
class Database
{
	private $driver;
	private $host;
	private $port;
	private $schema;
	private $username;
	private $password;
	private $connectDB;
	private $statement;
	private $query;

	private static $istance = null;

	/**
	 * Costruttore privato della classe, per la realizzazione del pattern singleton.
	 */
	private function __construct()
	{
		$this->driver	= BXD_DB_DRIVER;
		$this->host		= BXD_DB_HOST;
		$this->port		= BXD_DB_PORT;
		$this->schema	= BXD_DB_SCHEMA;
		$this->username = BXD_DB_USERNAME;
		$this->password = BXD_DB_PASSWORD;

		$this->inizializeConnection();
	}

	/**
	 * Restituisce l'istanza della classe Database qualora questa fosse già
	 * stata istanziata altrimenti viene creata una nuova e restituita.
	 *
	 * In caso di errore viene generata un'eccezione.
	 *
	 * @return Database L'istanza della classe Database per l'accesso alla base dati, in caso di errore viene generata un'eccezione.
	 *
	 */
	static function getInstance()
	{
		if (self::$istance === NULL)
			self::$istance = new Database();

		return self::$istance;
	}

	/**
	 * Inizializzazione della connesione al DB tramite la classe PDO
	 *
	 * Se la crezione della nuova istanza va a buon fine viene inizializzata la
	 *
	 * variabile $connectDB altrimenti viene stampato il messaggio di errore
	 */

	private function inizializeConnection()
	{
		$this->connectDB = $this->choseJustConnection();
	}

	/**
	 * Restituisce la connessione al DB con la classe PDO o ritorna un messaggio
	 *
	 * di errore sulla connessione
	 *
	 * @param string $driver
	 * @return <string/PDO>
	 */

	private function choseJustConnection()
	{
		switch ($this->driver)
		{
			case "pgsql":
				try
				{
					$dsn = "pgsql" . ":dbname=" . $this->schema . ";host=" . $this->host;
					$dbPDO = new PDO($dsn, $this->username, $this->password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
					return $dbPDO;
				}
				catch(PDOException $e)
				{
					BXD::newException("DatabaseConnectionError", $e->getMessage());
				}
				break;

			case "mysql":
				try
				{
					$dsn = "mysql" . ":host=" . $this->host . ";dbname=" . $this->schema;
					$dbPDO = new PDO($dsn, $this->username, $this->password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
					return $dbPDO;
				}
				catch(PDOException $e)
				{
					BXD::newException("DatabaseConnectionError", $e->getMessage());
				}
				break;
		}
	}

	/**
	 * Chiudo la connessione al DATABASE portando a null la variabile che
	 *
	 * contiene l'istanza della classe PDO
	 */

	public function closeDbConnection()
	{
		$this->connectDB = null;
	}

	/**
	 * Inizializzo una nuova transazione
	 */

	public function beginTransaction()
	{
		$this->connectDB->beginTransaction();
	}

	/**
	 * Salvo le modifiche in modo definitivo sul DB
	 */

	public function commit()
	{
		$this->connectDB->commit();
	}

	/**
	 * Annullo le modifiche e ripristino il DB allo stato precedente
	 */

	public function rollback()
	{
		$this->connectDB->rollBack();
	}

	/**
	 * Prepara la query per essere eseguita o filtrata. La query sarà scritta
	 *
	 * nel seguente modo:
	 *
	 * @example SELECT * FROM utenti WHERE id = :user_id
	 *
	 * dove :user_id è una variabile php dichiarata $user_id
	 *
	 * @param string $query
	 * @return TRUE se la preparazioen della query ha avuto successo, FALSE altrimenti.
	 */

	public function prepareQuery($query)
	{
		try
		{
			$this->query = $query;

			$this->statement = $this->connectDB->prepare($query);

			if ($this->statement === FALSE) return FALSE;

			return TRUE;
		}
		catch(PDOException $e)
		{
			echo $e->getMessage();

			return FALSE;
		};
	}

	/**
	 * I seguenti metodi con il suffisso bindParam identificano i metodi che
	 *
	 * servono per specificare il tipo di dato che deve riceve una determinato
	 *
	 * campo all'interno di una query SELECT
	 *
	 * @example
	 * QUERY = SELECT * FROM utenti WHERE id = :user_id
	 * bindParamInt($param, $value) -> $param = ":user_id" | $value = 1
	 *
	 * @param string $param
	 * @param <type> $value
	 */

	public function bindParamInt($param, $value)
	{
		$this->statement->bindParam($param, $value, PDO::PARAM_INT);
	}

	public function bindParamString($param, $value)
	{
		$this->statement->bindParam($param, $value, PDO::PARAM_STR);
	}

	public function bindParamBoolean($param, $value)
	{
		$this->statement->bindParam($param, $value, PDO::PARAM_BOOL);
	}

	public function bindParamGeneric($param, $value)
	{
		$this->statement->bindParam($param, $value);
	}

	/**
	 * Esegue la query memorizzata nello statement.
	 *
	 * @return TRUE se la query viene eseguita correttamente, FALSE altrimenti.
	 */

	public function executeQuery()
	{
		try
		{
			return $this->statement->execute();
		}
		catch(PDOException $e)
		{
			return FALSE;
		};
	}

	/**
	 * Ritorna l'ultimo errore prodotto.
	 *
	 * @return string la stringa contenente l'ultimo errore
	 */

	public function getLastError()
	{
		return $this->statement->errorInfo();
	}

	/**
	 * Restituisce la query eseguita
	 *
	 * @return <query>
	 */

	public function getResult()
	{
		return $this->statement;
	}

	/**
	 * Restituisce il numero delle righe contenute nel risultato della query
	 *
	 * @return int
	 */

	public function getNumRows()
	{
		return $this->statement->rowCount();
	}

	/**
	 * Restituisce l'id dell'utlima operazione di INSERT effettuata
	 *
	 * @return int
	 */

	public function getLastInsertId()
	{
		return $this->connectDB->lastInsertId();
	}

	/**
	 * Restituisce un array associativo con i risultati della query. Ogni chiave
	 *
	 * dell'array corrisponde al nome effettivo della colonna presente nella query
	 *
	 * @return <array>
	 */

	public function getArrayAssocResult()
	{
		return $this->statement->fetch(PDO::FETCH_ASSOC);
	}

	public function getFetchAll()
	{
		return $this->statement->fetchAll(PDO::FETCH_ASSOC);
	}

	public function getConnectDB()
	{
		return $this->connectDB;
	}

	public function exec($query)
	{
		$query = $this->connectDB->exec($query);
		$error = $this->connectDB->errorInfo();

		if($error[2] !== NULL)
			BXD::newException("DatabaseError", $error[2]);

		return $query;
	}
}
?>